package com.rex.saas.service;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import com.rex.saas.bean.*;
import com.rex.saas.bean.request.ProductKeyAddRequest;
import com.rex.saas.bean.response.*;
import com.rex.saas.constants.Constants;
import com.rex.saas.db.entity.*;
import com.rex.saas.db.mapper.*;
import com.rex.saas.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @ClassName TslService
 * @Description:
 * @Author: zhusiyu
 * @CreateDate: 2024/12/18 11:06	//创建时间
 */
@Component
@Slf4j
public class TslModelService {
    @Autowired
    private SaasTslModelMapper saasTslModelMapper;

    @Autowired
    private SaasTslModelPropertyMapper saasTslModelPropertyMapper;

    @Autowired
    private SaasTslModelServiceMapper saasTslModelServiceMapper;

    @Autowired
    private SaasTslModelServiceDataMapper saasTslModelServiceDataMapper;

    @Autowired
    private SaasTslModelEventMapper saasTslModelEventMapper;

    @Autowired
    private SaasTslModelEventDataMapper saasTslModelEventDataMapper;

    private static final Cache<Object, Object> tslModelCache = Caffeine.newBuilder()
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .maximumSize(1000)
            .build();

    @Transactional
    public boolean delete(String productKey){
        LambdaQueryWrapper<SaasTslModel> allQueryWrapper = new LambdaQueryWrapper<>();
        allQueryWrapper.eq(SaasTslModel::getProductKey, productKey);
        List<SaasTslModel> list = saasTslModelMapper.selectList(allQueryWrapper);
        if(list.isEmpty()){
            log.warn("getTslModel is empty error, productKey:{}", productKey);
            return true;
        }
        tslModelCache.invalidate(productKey);
        SaasTslModel saasTslModel = list.get(0);
        Long tslModelId = saasTslModel.getId();

        List<SaasTslModelService> serviceList = saasTslModelServiceMapper.selectList(new LambdaQueryWrapper<SaasTslModelService>().eq(SaasTslModelService::getTslModelId, saasTslModel.getId()));
        List<Long> serviceIdList = serviceList.stream().map(SaasTslModelService::getId).collect(Collectors.toList());
        saasTslModelServiceDataMapper.delete(new LambdaQueryWrapper<SaasTslModelServiceData>().in(SaasTslModelServiceData::getTslServiceId, serviceIdList));
        saasTslModelServiceMapper.delete(new LambdaQueryWrapper<SaasTslModelService>().eq(SaasTslModelService::getTslModelId, tslModelId));

        List<SaasTslModelEvent> eventList = saasTslModelEventMapper.selectList(new LambdaQueryWrapper<SaasTslModelEvent>().eq(SaasTslModelEvent::getTslModelId, tslModelId));
        List<Long> eventIdList = eventList.stream().map(SaasTslModelEvent::getId).collect(Collectors.toList());
        saasTslModelEventDataMapper.delete(new LambdaQueryWrapper<SaasTslModelEventData>().in(SaasTslModelEventData::getTslEventId, eventIdList));
        saasTslModelEventMapper.delete(new LambdaQueryWrapper<SaasTslModelEvent>().eq(SaasTslModelEvent::getTslModelId, tslModelId));

        saasTslModelPropertyMapper.delete(new LambdaQueryWrapper<SaasTslModelProperty>().eq(SaasTslModelProperty::getTslModelId, tslModelId));
        return saasTslModelMapper.delete(new LambdaQueryWrapper<SaasTslModel>().eq(SaasTslModel::getId, tslModelId))==1;

    }
    public TslModelResponse detail(String productKey, String tenantId){
        LambdaQueryWrapper<SaasTslModel> allQueryWrapper = new LambdaQueryWrapper<>();
        allQueryWrapper.eq(SaasTslModel::getProductKey, productKey);
        allQueryWrapper.eq(SaasTslModel::getTenantId, tenantId);
        List<SaasTslModel> list = saasTslModelMapper.selectList(allQueryWrapper);
        if(list.isEmpty()){
            log.warn("detail is empty error, productKey:{}", productKey);
            tslModelCache.put(productKey, null);
            return null;
        }
        SaasTslModel saasTslModel = list.get(0);
        TslModelResponse tslModel = new TslModelResponse();
        BeanUtils.copyProperties(saasTslModel, tslModel);

        List<TslPropertyResponse> properties = new ArrayList<>();
        List<SaasTslModelProperty> propertyList = saasTslModelPropertyMapper.selectList(new LambdaQueryWrapper<SaasTslModelProperty>().eq(SaasTslModelProperty::getTslModelId, saasTslModel.getId()));
        for (SaasTslModelProperty saasTslModelProperty : propertyList){
            TslPropertyResponse tslProperty = new TslPropertyResponse();
            BeanUtils.copyProperties(saasTslModelProperty, tslProperty);
            String dataType = saasTslModelProperty.getDataType();
            tslProperty.setDataType(JSONObject.parseObject(dataType));
            properties.add(tslProperty);
        }
        tslModel.setProperties(JSONArray.parseArray(JSON.toJSONString(properties)));

        List<TslServiceResponse> services = new ArrayList<>();
        List<SaasTslModelService> serviceList = saasTslModelServiceMapper.selectList(new LambdaQueryWrapper<SaasTslModelService>().eq(SaasTslModelService::getTslModelId, saasTslModel.getId()));
        for(SaasTslModelService saasTslModelService : serviceList){
            TslServiceResponse tslService = new TslServiceResponse();
            BeanUtils.copyProperties(saasTslModelService, tslService);
            List<TslServiceDataResponse> inputData = new ArrayList<>();
            List<TslServiceDataResponse> outputData = new ArrayList<>();
            List<SaasTslModelServiceData> inputDataList = saasTslModelServiceDataMapper.selectList(new LambdaQueryWrapper<SaasTslModelServiceData>().eq(SaasTslModelServiceData::getTslServiceId, saasTslModelService.getId()));
            for (SaasTslModelServiceData saasTslModelServiceData : inputDataList){
                TslServiceDataResponse tslServiceData = new TslServiceDataResponse();
                BeanUtils.copyProperties(saasTslModelServiceData, tslServiceData);
                if(saasTslModelServiceData.getType()==1){
                    inputData.add(tslServiceData);
                }else if(saasTslModelServiceData.getType()==2){
                    outputData.add(tslServiceData);
                }
            }
            tslService.setInputData(JSONArray.parseArray(JSON.toJSONString(inputData)));
            tslService.setOutputData(JSONArray.parseArray(JSON.toJSONString(outputData)));
            services.add(tslService);
        }
        tslModel.setServices(JSONArray.parseArray(JSON.toJSONString(services)));

        List<TslEventResponse> events = new ArrayList<>();
        List<SaasTslModelEvent> eventList = saasTslModelEventMapper.selectList(new LambdaQueryWrapper<SaasTslModelEvent>().eq(SaasTslModelEvent::getTslModelId, saasTslModel.getId()));
        for (SaasTslModelEvent saasTslModelEvent : eventList){
            TslEventResponse tslEvent = new TslEventResponse();
            BeanUtils.copyProperties(saasTslModelEvent, tslEvent);
            List<TslEventDataResponse> outputData = new ArrayList<>();
            List<SaasTslModelEventData> inputDataList = saasTslModelEventDataMapper.selectList(new LambdaQueryWrapper<SaasTslModelEventData>().eq(SaasTslModelEventData::getTslEventId, saasTslModelEvent.getId()));
            for (SaasTslModelEventData saasTslModelEventData : inputDataList){
                TslEventDataResponse tslEventData = new TslEventDataResponse();
                BeanUtils.copyProperties(saasTslModelEventData, tslEventData);
                if(saasTslModelEventData.getType()==1){
                }else if(saasTslModelEventData.getType()==2){
                    outputData.add(tslEventData);
                }
            }
            tslEvent.setOutputData(JSONArray.parseArray(JSON.toJSONString(outputData)));
        }
        tslModel.setEvents(JSONArray.parseArray(JSON.toJSONString(events)));
        tslModelCache.put(productKey, tslModel);
        return tslModel;
    }

    @Transactional
    public boolean add(ProductModel productModel, String tenantId) {
        SaasTslModel saasTslModel = new SaasTslModel();
        BeanUtils.copyProperties(productModel, saasTslModel);
        saasTslModel.setTenantId(tenantId);
        if(saasTslModelMapper.insert(saasTslModel)!=1){
            log.error("add tslModel error, tslModel:{}", productModel);
            throw new RuntimeException("add tslModel error");
        }
        long tslModelid = saasTslModel.getId();
        for (TslProperty tslProperty : productModel.getProperties()) {
            SaasTslModelProperty saasTslModelProperty = new SaasTslModelProperty();
            BeanUtils.copyProperties(tslProperty, saasTslModelProperty);
            saasTslModelProperty.setTslModelId(tslModelid);
            if (saasTslModelPropertyMapper.insert(saasTslModelProperty)!=1){
                log.error("add tslModelProperty error, tslModelProperty:{}", tslProperty);
                throw new RuntimeException("add tslModelProperty error");
            }
        }

        for (TslService tslService : productModel.getServices()) {
            SaasTslModelService saasTslModelService = new SaasTslModelService();
            BeanUtils.copyProperties(tslService, saasTslModelService);
            saasTslModelService.setTslModelId(tslModelid);
            if (saasTslModelServiceMapper.insert(saasTslModelService)!=1){
                log.error("add tslModelService error, tslModelService:{}", tslService);
                throw new RuntimeException("add tslModelService error");
            }
            List<TslServiceData> inputData = tslService.getInputData();
            if(!CollectionUtils.isEmpty(inputData)){
                for(TslServiceData tslServiceData : inputData){
                    SaasTslModelServiceData saasTslModelServiceData = new SaasTslModelServiceData();
                    BeanUtils.copyProperties(tslServiceData, saasTslModelServiceData);
                    saasTslModelServiceData.setTslServiceId(saasTslModelService.getId());
                    saasTslModelServiceData.setType(1);
                    if (saasTslModelServiceDataMapper.insert(saasTslModelServiceData)!=1){
                        log.error("add tslModelServiceData inputData error, tslModelServiceData:{}", tslServiceData);
                        throw new RuntimeException("add tslModelServiceData inputData error");
                    }
                }
            }

            List<TslServiceData> outputData = tslService.getOutputData();
            if(!CollectionUtils.isEmpty(outputData)){
                for(TslServiceData tslServiceData : outputData){
                    SaasTslModelServiceData saasTslModelServiceData = new SaasTslModelServiceData();
                    BeanUtils.copyProperties(tslServiceData, saasTslModelServiceData);
                    saasTslModelServiceData.setTslServiceId(saasTslModelService.getId());
                    saasTslModelServiceData.setType(2);
                    if (saasTslModelServiceDataMapper.insert(saasTslModelServiceData)!=1){
                        log.error("add tslModelServiceData outputData error, tslModelServiceData:{}", tslServiceData);
                        throw new RuntimeException("add tslModelServiceData outputData error");
                    }
                }
            }
        }

        for (TslEvent tslEvent : productModel.getEvents()) {
            SaasTslModelEvent saasTslModelEvent = new SaasTslModelEvent();
            BeanUtils.copyProperties(tslEvent, saasTslModelEvent);
            saasTslModelEvent.setTslModelId(tslModelid);
            if (saasTslModelEventMapper.insert(saasTslModelEvent)!=1){
                log.error("add tslModelEvent error, tslModelEvent:{}", tslEvent);
                throw new RuntimeException("add tslModelEvent error");
            }

            List<TslEventData> inputData = tslEvent.getInputData();
            if(!CollectionUtils.isEmpty(inputData)){
                for(TslEventData tslEventData : inputData){
                    SaasTslModelEventData saasTslModelEventData = new SaasTslModelEventData();
                    BeanUtils.copyProperties(tslEventData, saasTslModelEventData);
                    saasTslModelEventData.setTslEventId(saasTslModelEvent.getId());
                    saasTslModelEventData.setType(1);
                    if (saasTslModelEventDataMapper.insert(saasTslModelEventData)!=1){
                        log.error("add saasTslModelEventData inputData error, saasTslModelEventData:{}", tslEventData);
                        throw new RuntimeException("add saasTslModelEventData inputData error");
                    }
                }
            }

            List<TslEventData> outputData = tslEvent.getOutputData();
            if(!CollectionUtils.isEmpty(outputData)){
                for(TslEventData tslEventData : outputData){
                    SaasTslModelEventData saasTslModelEventData = new SaasTslModelEventData();
                    BeanUtils.copyProperties(tslEventData, saasTslModelEventData);
                    saasTslModelEventData.setTslEventId(saasTslModelEvent.getId());
                    saasTslModelEventData.setType(2);
                    if (saasTslModelEventDataMapper.insert(saasTslModelEventData)!=1){
                        log.error("add saasTslModelEventData outputData error, saasTslModelEventData:{}", tslEventData);
                        throw new RuntimeException("add saasTslModelEventData outputData error");
                    }
                }
            }
        }
        tslModelCache.invalidate(productModel.getProductKey());
        return true;
    }

    public boolean updateProperty(TslProperty tslProperty) {
        SaasTslModelProperty tslModelProperty = new SaasTslModelProperty();
        BeanUtils.copyProperties(tslProperty, tslModelProperty);
        tslModelProperty.setGmtModified(new Date());
        saasTslModelPropertyMapper.updateById(tslModelProperty);
        return true;
    }

    public SaasTslModel queryTslModelById(Long tslModelId) {
        return saasTslModelMapper.selectById(tslModelId);
    }

    public SaasTslModelProperty queryTslModelPropertyById(Long Id) {
        return saasTslModelPropertyMapper.selectById(Id);
    }

    public boolean init(String path, String token) {
        File folder = new File(path);
        File[] listOfFiles = folder.listFiles();
        if (listOfFiles == null){
            log.error("path:{} is not exist", path);
            return false;
        }
        for (File file : listOfFiles) {
            if (!file.isFile()){
                log.error("file:{} is not file", file.getName());
                continue;
            }
            try{
                String content = new String(Files.readAllBytes(Paths.get(file.getAbsolutePath())), StandardCharsets.UTF_8);
                JSONObject jsonObject = JSONObject.parseObject( content);
                ProductModel productModel = new ProductModel() ;
                log.info("file:{} content:{}", file.getName(), jsonObject);
                JSONObject profile = jsonObject.getJSONObject("profile");
                productModel.setProfile(profile.getString("name"));
                productModel.setProductKey(profile.getString("productKey"));
                productModel.setVersion(profile.getString(Constants.Version));

                JSONArray propertiesObj = jsonObject.getJSONArray("properties");
                if (propertiesObj != null){
                    List<TslProperty> properties = new ArrayList<>();
                    for (int i = 0; i < propertiesObj.size(); i++){
                        TslProperty tslProperty = new TslProperty();
                        JSONObject property = propertiesObj.getJSONObject(i);
                        tslProperty.setName(property.getString("name"));
                        tslProperty.setIdentifier(property.getString("identifier"));
                        tslProperty.setAccessMode(property.getString("accessMode"));
                        tslProperty.setRequired(property.getBoolean("required"));
                        tslProperty.setDataType(property.getString("dataType"));
                        properties.add(tslProperty);
                    }
                    productModel.setProperties(properties);
                }

                JSONArray servicesObj = jsonObject.getJSONArray("services");
                if (servicesObj != null){
                    List<TslService> services = new ArrayList<>();
                    for (int i = 0; i < servicesObj.size(); i++){
                        TslService tslService = new TslService();
                        JSONObject property = servicesObj.getJSONObject(i);
                        tslService.setName(property.getString("name"));
                        tslService.setIdentifier(property.getString("identifier"));
                        tslService.setCallType(property.getString("callType"));
                        tslService.setRequired(property.getBoolean("required"));
                        tslService.setMethod(property.getString("method"));
                        services.add(tslService);
                        JSONArray inputDataObj = property.getJSONArray("inputData");
                        if (inputDataObj != null){
                            List<TslServiceData> inputDatas = new ArrayList<>();
                            for (int j = 0; j < inputDataObj.size(); j++){
                                TslServiceData tslServiceData = new TslServiceData();
                                JSONObject inputData = inputDataObj.getJSONObject(j);
                                tslServiceData.setName(inputData.getString("name"));
                                tslServiceData.setIdentifier(inputData.getString("identifier"));
                                tslServiceData.setDataType(inputData.getString("dataType"));
                                tslServiceData.setType(Constants.INPUT);
                                inputDatas.add(tslServiceData);
                            }
                            tslService.setInputData(inputDatas);
                        }
                        JSONArray outputDataObj = property.getJSONArray("outputData");
                        if (outputDataObj != null){
                            List<TslServiceData> outputDatas = new ArrayList<>();
                            for (int j = 0; j < outputDataObj.size(); j++){
                                TslServiceData tslServiceData = new TslServiceData();
                                JSONObject outputData = outputDataObj.getJSONObject(j);
                                tslServiceData.setName(outputData.getString("name"));
                                tslServiceData.setIdentifier(outputData.getString("identifier"));
                                tslServiceData.setDataType(outputData.getString("dataType"));
                                tslServiceData.setType(Constants.OUTPUT);
                                outputDatas.add(tslServiceData);
                            }
                            tslService.setOutputData(outputDatas);
                        }
                    }
                    productModel.setServices(services);
                }

                JSONArray eventsObj = jsonObject.getJSONArray("events");
                if (eventsObj != null){
                    List<TslEvent> events = new ArrayList<>();
                    for (int i = 0; i < eventsObj.size(); i++){
                        TslEvent tslEvent = new TslEvent();
                        JSONObject event = eventsObj.getJSONObject(i);
                        tslEvent.setName(event.getString("name"));
                        tslEvent.setIdentifier(event.getString("identifier"));
                        tslEvent.setType(event.getString("type"));
                        tslEvent.setRequired(event.getBoolean("required"));
                        tslEvent.setMethod(event.getString("method"));
                        JSONArray outputDataObj = event.getJSONArray("outputData");
                        if (outputDataObj != null){
                            List<TslEventData> outputDatas = new ArrayList<>();
                            for (int j = 0; j < outputDataObj.size(); j++){
                                TslEventData tslEventData = new TslEventData();
                                JSONObject outputData = outputDataObj.getJSONObject(j);
                                tslEventData.setName(outputData.getString("name"));
                                tslEventData.setIdentifier(outputData.getString("identifier"));
                                tslEventData.setDataType(outputData.getString("dataType"));
                                tslEventData.setType(Constants.OUTPUT);
                                outputDatas.add(tslEventData);
                            }
                            tslEvent.setOutputData(outputDatas);
                        }
                        events.add(tslEvent);
                    }
                    productModel.setEvents(events);
                }

                if (add(productModel, JwtUtils.getTenantId(token))){
                    log.info("file:{} add success", file.getName());
                }else{
                    log.error("file:{} add error", file.getName());
                }
            }catch (Exception e){
                log.error("file:{} read error", file.getName(), e);
            }
        }
        return true;
    }

    public List<SaasTslModel> queryTslModelByPK(String productKey, String tenantId) {
        return saasTslModelMapper.selectList(new LambdaQueryWrapper<SaasTslModel>()
                .eq(SaasTslModel::getProductKey, productKey).eq(SaasTslModel::getTenantId, tenantId));
    }

    public List<SaasTslModel> queryTslModelByName(String productName, String tenantId) {
        return saasTslModelMapper.selectList(new LambdaQueryWrapper<SaasTslModel>()
                .eq(SaasTslModel::getModelName, productName).eq(SaasTslModel::getTenantId, tenantId));
    }

    public Boolean addProductModel(ProductKeyAddRequest productKeyAddRequest, String tenantId) {
        SaasTslModel saasTslModel = new SaasTslModel();
        BeanUtils.copyProperties(productKeyAddRequest, saasTslModel);
        return saasTslModelMapper.insert(saasTslModel)==1;
    }


    public Boolean configProperty(TslProperty tslProperty) {
        SaasTslModelProperty saasTslModelProperty = new SaasTslModelProperty();
        BeanUtils.copyProperties(tslProperty, saasTslModelProperty);
        return saasTslModelPropertyMapper.insert(saasTslModelProperty)==1;
    }

    public List<SaasTslModelProperty> queryPropertyByIdentifier(String identifier, Long modelId) {
        return saasTslModelPropertyMapper.selectList(new LambdaQueryWrapper<SaasTslModelProperty>()
                .eq(SaasTslModelProperty::getIdentifier, identifier).eq(SaasTslModelProperty::getTslModelId, modelId));
    }

    public List<SaasTslModel> queryTslModelByCategoryName(String airCon, String tenantId) {
        return saasTslModelMapper.selectList(new LambdaQueryWrapper<SaasTslModel>()
                .eq(SaasTslModel::getProfile, airCon).eq(SaasTslModel::getTenantId, tenantId));
    }
}
